#!/usr/bin/python2.4

# pylint: disable-msg=C6304

"""Tests operations on geometric classes and functions from matrix.py."""

import math
import StringIO
import unittest

from matrix import DM
from matrix import GreatCircleDistance
from matrix import LatLon
from matrix import LinesIntersect
from matrix import Matrix
from matrix import MinutesAsRadians
from matrix import Point
from matrix import Polar
from matrix import Ratio


class MiscRoutineTester(unittest.TestCase):

  def testRatio(self):
    self.assertEqual(3.0, Ratio(0.0, 3.0, 10.0, 0, 10.0))
    self.assertEqual(1.5, Ratio(0.0, 3.0, 10.0, 0, 5.0))
    self.assertEqual(7.0, Ratio(10.0, 3.0, 0.0, 0, 10.0))

  def testPolar(self):
    p1 = Polar(1.0, 0.0)
    p2 = Polar(1.0, 360.0)
    self.assertEqual(p1, p2)
    self.assertEqual(1.0, p1.Mag())
    self.assertEqual(1.0, p1.Magnitude())
    self.assertEqual(1.0, p1.Rho())
    self.assertEqual(0.0, p1.Angle())
    self.assertEqual(0.0, p2.Theta())
    self.assertEqual('(01.00 000)', str(p1))
    self.assertEqual('01.00,000', p1.CSVFormat())
    self.assertEqual('Polar(01.00,000)', p1.ConstructorFormat())
    self.assertEqual(
        Polar.BuildFromCSV([17, 1.0, 0.0], 1),
        (p1, 2))

    p = Polar(1.0, 720)
    self.assertEqual(0, p.Angle())
    p = Polar(1.0, 721)
    self.assertEqual(1, p.Angle())
    p = Polar(1.0, 719)
    self.assertEqual(359, p.Angle())
    p = Polar(1.0, -90)
    self.assertEqual(270, p.Angle())
    p = Polar(1.0, -179)
    self.assertEqual(181, p.Angle())
    p = Polar(1.0, 179)
    self.assertEqual(179, p.Angle())

  def testPoint(self):
    p1 = Point(0, 0)
    p2 = Point(0, 1)
    p3 = Point(1, 1)
    self.assertAlmostEqual(1.0, p1.DistanceFrom(p2), 5)
    self.assertAlmostEqual(1.414213, p1.DistanceFrom(p3), 5)
    self.assertAlmostEqual(2.0, p1.SquareDistance(p3), 5)
    self.assertAlmostEqual(180.0, p1.AngleFrom(p2), 5)  # TODO(rrh): odd?
    self.assertAlmostEqual(0.0, p2.KeyByAxis(0))
    self.assertAlmostEqual(1.0, p2.KeyByAxis(1))
    self.assertEqual('(   0,    1)', str(p2))

  def testLatLon(self):
    x = LatLon(DM(48, 12), -DM(124, 40))
    y = LatLon(DM(49, 12), -DM(124, 40))  # y removed from x by 1 degree lat
    self.assertEqual('(048:12, 4:40)', str(x))
    self.assertEqual('(048:12N, 124:40W)', x.FullPrint())
    self.assertEqual('LatLon( DM( 48, 12), -DM(124, 40))', x.DMFormat())
    self.assertEqual('1,048,12, -1,124,40', x.CSVFormat())
    self.assertEqual(
        LatLon.BuildFromCSV([17, 1, 48, 12, -1, 124, 40], 1),
        (x, 6))
    self.assertEqual(x.KeyByAxis(1), 48*60 + 12)  # latitude (y)
    self.assertEqual(x.KeyByAxis(0), -(124*60 + 40))  # longitude (x)
    self.assertAlmostEqual(x.SquareDistance(x), 0, 10)
    self.assertAlmostEqual(x.SquareDistance(y), 3600, 3)

  def testOther(self):
    self.assertAlmostEqual(math.pi, MinutesAsRadians(60 * 180), 7)

  def testMatrix(self):
    m = Matrix(2.0)
    m.Print(StringIO.StringIO())

  def testPolarToRectangular(self):
    p = Polar(10, 0)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(0, 10).x, prect.x)
    self.assertAlmostEqual(Point(0, 10).y, prect.y)

    p = Polar(10, 90)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(10, 0).x, prect.x)
    self.assertAlmostEqual(Point(10, 0).y, prect.y)

    p = Polar(10, 180)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(0, -10).x, prect.x)
    self.assertAlmostEqual(Point(0, -10).y, prect.y)

    p = Polar(10, 270)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(-10, 0).x, prect.x)
    self.assertAlmostEqual(Point(-10, 0).y, prect.y)

    p = Polar(10, 30)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(5.000, 8.660254).x, prect.x, 4)
    self.assertAlmostEqual(Point(5.000, 8.660254).y, prect.y, 4)

    p = Polar(10, 180 - 30)
    prect = p.toRectangular()
    self.assertAlmostEqual(Point(5.000, -8.660254).x, prect.x, 4)
    self.assertAlmostEqual(Point(5.000, -8.660254).y, prect.y, 4)

    p = Polar(10, 0) + Polar(-5, 0)
    self.assertAlmostEquals(p.Mag(), 5.0, 4)
    self.assertAlmostEquals(p.Angle(), 0.0, 4)

    p = Polar(10, 0) + Polar(10, 180)
    self.assertAlmostEquals(p.Mag(), 0.0, 4)
    self.assertAlmostEquals(p.Angle(), 90.0, 4)  # perhaps

    p = Polar(10, 0) + Polar(10, 90)
    self.assertAlmostEquals(p.Mag(), 10*math.sqrt(2.0), 4)
    self.assertAlmostEquals(p.Angle(), 45.0, 4)

    p = Polar(10, 0) + Polar(10, 270)
    self.assertAlmostEquals(p.Mag(), 10*math.sqrt(2.0), 4)
    self.assertAlmostEquals(p.Angle(), 360-45.0, 4)

    p = Polar(10, 180) + Polar(10, 270)
    self.assertAlmostEquals(p.Mag(), 10*math.sqrt(2.0), 4)
    self.assertAlmostEquals(p.Angle(), 180+45.0, 4)

    p = Polar(0, 0) + Polar(10, 90)
    self.assertAlmostEquals(p.Mag(), 10, 4)
    self.assertAlmostEquals(p.Angle(), 90.0, 4)

    p = Polar(10, 0) - Polar(10, 0)
    self.assertAlmostEquals(p.Mag(), 0.0, 4)
    self.assertAlmostEquals(p.Angle(), 0.0, 4)  # perhaps

    p = Polar(10, 90) - Polar(10, 0)
    self.assertAlmostEquals(p.Mag(), 10*math.sqrt(2.0))
    self.assertAlmostEquals(p.Angle(), 135, 4)


class GreatCircleTester(unittest.TestCase):
  """Test the great circle algorithms."""

  def testAll(self):
    """Test that the great circle computation returns plausible results."""
    # Expect 1nm, expect  90 degrees
    r, b = GreatCircleDistance(LatLon(0, 0), LatLon(0, 1))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 90, 2)

    # Expect 1nm, expect 270 degrees
    r, b = GreatCircleDistance(LatLon(0, 0), LatLon(0, -1))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 270, 2)

    # Expect 1nm, expect 270 degrees
    r, b = GreatCircleDistance(LatLon(0, 1), LatLon(0, 0))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 270, 2)

    # Expect 1nm, expect 90 degrees
    r, b = GreatCircleDistance(LatLon(0, -1), LatLon(0, 0))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 90, 2)

    # Expect 1nm, 0 degrees
    r, b = GreatCircleDistance(LatLon(0, 0), LatLon(1, 0))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 0, 2)

    # Expect 1nm, 180 degrees
    r, b = GreatCircleDistance(LatLon(1, 0), LatLon(0, 0))
    self.assertAlmostEquals(r, 1, 2)
    self.assertAlmostEquals(b, 180, 2)

    # Expect 1.414nm 45 degrees
    r, b = GreatCircleDistance(LatLon(0, 0), LatLon(1, 1))
    self.assertAlmostEquals(r, 1.414, 2)
    self.assertAlmostEquals(b, 45, 2)

    # Expect 60nm, angle 0 degrees
    r, b = GreatCircleDistance(
        LatLon(DM(45, 0), DM(0, 0)),
        LatLon(DM(46, 0), DM(0, 0)))
    self.assertAlmostEquals(r/1e02, 60/1e02, 2)
    self.assertAlmostEquals(b, 0, 2)

    # Worked example from Wikipedia; expect 1558.9nm
    # See http://en.wikipedia.org/wiki/Great-circle_navigation
    r, b = GreatCircleDistance(
        LatLon(36.12*60.0, -86.67*60.0),  # BNA
        LatLon(33.94*60.0, -118.40*60.0)  # LAX
        )
    self.assertAlmostEquals(r/1e04, 1558/1e04, 3)
    # self.assertAlmostEquals(b, 0, 2)

    # Worked example from Wikipedia; expect 1558.9nm, perhaps 260degrees
    # See http://en.wikipedia.org/wiki/Great-circle_navigation
    r, b = GreatCircleDistance(
        LatLon(DM(36, 7.2), -DM(86, 40.2)),  # BNA
        LatLon(DM(33, 56.4), -DM(118, 24.0)),  # LAX
        )
    self.assertAlmostEquals(r/1e04, 1558/1e04, 3)


class LineIntersectTester(unittest.TestCase):
  """Test the line intersection engine."""

  def testIntersect(self):
    self.assertTrue(LinesIntersect(
        Point(-1, 0), Point(1, 0),
        Point(0, 1), Point(0, -1)))


def main():
  unittest.main()


if __name__ == '__main__':
  main()
